/**
@file
	ModuleManager.cpp
@brief

@author
	GengYong
@revision
	2005/11/05 * Created by GengYong.
*/


#include "StdUtility.h"
#include "LogDevice.h"
#include "ModuleManager.h"

#include <cassert>

using namespace SAF;

ModuleManager::ModuleManager():m_treeRoot(0)
{
	PERFCOUNTER("ModuleManager::ModuleManager");
	return;
}

ModuleManager::~ModuleManager()
{
	PERFCOUNTER("ModuleManager::~ModuleManager");

	struct TreeDestroyer
	{	
		TreeDestroyer(ModuleManager * const This, ModuleManager::ModuleTree * node)
		{
			PERFCOUNTER("ModuleManager::~ModuleManager::TreeDestroyer");

			if (node != 0)
			{
				TreeDestroyer DestroyLeft(This, node->lleaf);
				TreeDestroyer DestroyRight(This, node->rleaf);

				(void)DestroyLeft;
				(void)DestroyRight;

				This->CleanModule(node->info);
				Util::Util_FreeString(node->name);
				delete node->info;
				delete node;
			}
			return;
		}
	} DestroyRoot(this, this->m_treeRoot);

	(void)DestroyRoot;

	this->m_treeRoot = 0;

	return;
}

void ModuleManager::Unload(const char * modname)
{
	PERFCOUNTER("ModuleManager::Unload");

	bool deasil = true;
	Util::CompareResult result = Util::equal;
	ModuleManager::ModuleTree *  node =  m_treeRoot;
	ModuleManager::ModuleTree ** slot = &m_treeRoot;
	while (node != 0)
	{
		result = Util::Util_CompareString(node->name, modname);

		if (Util::equal == result)
		{
			// rebuild tree
			if ((node->lleaf != 0) && (node->rleaf != 0))
			{
				if (deasil)
				{
					ModuleManager::ModuleTree * subtree = node->lleaf;
					while (subtree->rleaf != 0) subtree = subtree->rleaf;
					subtree->rleaf = node->rleaf;
					node = node->lleaf;
				}
				else
				{
					ModuleManager::ModuleTree * subtree = node->rleaf;
					while (subtree->lleaf != 0) subtree = subtree->lleaf;
					subtree->lleaf = node->lleaf;
					node = node->rleaf;
				}
			}
			else
			{
				node = (ModuleManager::ModuleTree*)(*(int*)(node->lleaf) | *(int*)(node->rleaf));
			}

			if ((*slot)->info != 0)
			{
				CleanModule((*slot)->info);
				delete (*slot)->info;
			}

			if ((*slot)->name != 0) 
			{
				Util::Util_FreeString((*slot)->name);
			}

			*slot = node;
			break;
		}
		else if (Util::less == result)
		{
			deasil = true;
			slot = &(node->rleaf);
			node = node->rleaf;
		}
		else
		{
			deasil = false;
			slot = &(node->lleaf);
			node = node->lleaf;
		}
	}
	return;
}

bool ModuleManager::Reload(const char * modname, const char * filename)
{
	PERFCOUNTER("ModuleManager::Reload");

	assert(modname != 0);
	assert(filename != 0);

	if ((modname != 0) && (filename != 0))
	{
		Util::DynamicLibrary * newdl = new Util::DynamicLibrary(filename);
		if (newdl != 0)
		{
			if (!newdl->IsLoaded())
			{
				delete newdl;
				return false;
			}

			// Show module infomation.
			SAF::DEFINE::MODULEDESCRIPTION * desc = 0;
			if ((desc = (SAF::DEFINE::MODULEDESCRIPTION*)(newdl->GetProcAddr(SAF::DEFINE::DESCRIPTIONNAME))) != NULL)
			{
				SAFLOG("Load library %s from %s:", modname, filename);
				SAFLOG("    Class: %s", desc->classname);
				SAFLOG("    Time: %s", desc->timestamp);
				SAFLOG("    Copy Rights: %s", desc->copyright);
				SAFLOG("    Description: %s", desc->description);
				SAFLOG("    Version: %lu", desc->version);
			}
			else
			{
				delete newdl;
				return false;
			}

			ModuleManager::Builder	builder = 0;
			if ((builder = (ModuleManager::Builder)(newdl->GetProcAddr(SAF::DEFINE::BUILDERNAME))) == NULL)
			{
				delete newdl;
				return false;
			}
			ModuleManager::Destroyer destroyer = 0;
			if ((destroyer = (ModuleManager::Destroyer)(newdl->GetProcAddr(SAF::DEFINE::DESTROYERNAME))) == NULL)
			{
				delete newdl;
				return false;
			}

			// module successful loaded and checked pass
			ModuleManager::ModuleInfo * info = new ModuleManager::ModuleInfo();
			if (info != 0)
			{
				//========================================================
				// create
				info->deprecated = false;
				info->counter	 = 0;
				info->builder	 = (void*)(builder);
				info->destroyer  = (void*)(destroyer);
				info->holder	 = newdl;
				info->classname  = desc->classname;
				info->imagebase  = 0;
				info->imagesize  = 0;
				info->signature  = 0;
				info->timestamp  = 0;
				info->version	 = desc->version;
				//========================================================
				ModuleManager::ModuleTree **  pnode =  &m_treeRoot;
				Util::CompareResult result = Util::equal;
				while (((*pnode) != 0) && (Util::equal != (result = Util::Util_CompareString((*pnode)->name, modname))))
				{
					pnode = (Util::less == result ? &((*pnode)->rleaf) : &((*pnode)->lleaf));
				}

				if (0 == (*pnode))
				{
					if(0 != ((*pnode) = new ModuleManager::ModuleTree()))
					{	
						(*pnode)->lleaf = 0;
						(*pnode)->rleaf = 0;

						if (((*pnode)->name = Util::Util_AllocString(modname)) != 0)
						{
							(*pnode)->info = info;
							return true;
						}

						delete (*pnode);
						*pnode = 0;
					}
				}
				else
				{
					CleanModule((*pnode)->info);
					delete (*pnode)->info;
					(*pnode)->info = info;
					return true;
				}

				CleanModule(info);
				delete(info);
			}
		}
	}
	return false;
}


ModuleManager::ModuleInfo * ModuleManager::GetModuleInfo(const char * modname)
{
	PERFCOUNTER("ModuleManager::GetLibraryInfo");

	if (modname != 0)
	{
		ModuleManager::ModuleTree * node = m_treeRoot;
		for (; node != 0;)
		{
			Util::CompareResult result = Util::Util_CompareString(node->name, modname);
			if (Util::equal == result)
			{
				return node->info;
			}
			else
			{
				node = (Util::less == result ? node->rleaf : node->lleaf);
			}
		}
	}
	return 0;
}

void * ModuleManager::CreateObject(const char * name)
{
	PERFCOUNTER("ModuleManager::CreateObject");

	ModuleManager::ModuleInfo * modinfo = GetModuleInfo(name);
	if (modinfo != 0 && modinfo->builder != 0)
	{
		void * handle = ((ModuleManager::Builder)(modinfo->builder))(SAF::DEFINE::MODULEDESCRIPTION::VERSION, 0);
		if (0 != handle)
		{
			modinfo->counter++;
			return handle;
		}
	}
	return 0;
}

void ModuleManager::DestroyObject(const char * name, void * object)
{
	PERFCOUNTER("ModuleManager::DestroyObject");

	ModuleManager::ModuleInfo * modinfo = GetModuleInfo(name);
	if (modinfo != 0 && modinfo->destroyer != 0 && modinfo->counter > 0)
	{
		modinfo->counter--;
		((ModuleManager::Destroyer)(modinfo->destroyer))(object);
	}
	return;
}

bool ModuleManager::CleanModule(ModuleManager::ModuleInfo * info)
{
	PERFCOUNTER("ModuleManager::_CleanModule");

	assert(info != 0);

	if (info != 0)
	{
		if (info->counter <= 0)
		{
			delete (Util::DynamicLibrary *)(info->holder);
			info->classname  = 0;
			info->holder	 = 0;
			info->counter	 = 0;
			info->deprecated = 0;
			info->builder	 = 0;
			info->destroyer	 = 0;
			info->imagebase  = 0;
			info->imagesize  = 0;
			info->signature  = 0;
			info->timestamp  = 0;
			info->version	 = 0;
			return true;
		}
		else
		{
			info->deprecated = true;
			return false;
		}
	}
	return false;
}

